﻿using System;
using System.Collections.Generic;
using Microsoft.Win32;
using Microsoft.MediaPlayer.Interop;

namespace BMproductions.Media.MediaPlayer.WMPPlus {
	public interface IPluginManager {
		void Register(WMPPlugin plugin);
		void Unregister(WMPPlugin plugin);
		void LoadPlugin(WMPPlugin plugin);
		void UnloadPlugin(WMPPlugin plugin);
	}

	public class PluginManager: IDisposable, IEnumerable<WMPPlugin> {
		IDictionary<Guid, WMPPlugin> plugins = new Dictionary<Guid, WMPPlugin>();
		ICollection<Guid> clsids = new HashSet<Guid>();
		ICollection<IPluginManager> modules = new List<IPluginManager>();

		public PluginManager() {}

		public void Dispose() {
			foreach (WMPPlugin plugin in this.plugins.Values) {
				foreach (var module in this.modules) {
					module.Unregister(plugin);
				}
				plugin.manager = null;
			}
			this.clsids.Clear();
			this.plugins.Clear();
			this.modules.Clear();
		}

		public WMPPlugin Find(Guid clsid, bool create) {
			WMPPlugin plugin;
			if (this.plugins.TryGetValue(clsid, out plugin))
				return plugin;
			if (create)
				return Create(clsid);
			return null;
		}

		public void WrapPlugin(Guid clsid, IntPtr plugin, IWMPPluginUI intf) {
			var item = Find(clsid, true);
			if (item != null) {
				item.Native = plugin;
				item.Original = intf;
			}
		}

		internal void SetupPlugin(WMPPlugin plugin, bool shutdown) {
			foreach (var module in this.modules) {
				if (shutdown)
					module.UnloadPlugin(plugin);
				else
					module.LoadPlugin(plugin);
			}
		}

		internal void SetupPlugin(Guid plugin, bool shutdown) {
			WMPPlugin managed = this.plugins[plugin];
			managed.Loaded = !shutdown;
			SetupPlugin(managed, shutdown);
		}

		public void Init() {
			this.clsids.Clear();
			var root = Registry.LocalMachine;
			string[] keys;
			using (var key = root.OpenSubKey(WMPConstants.PLUGIN_INSTALLREGKEY)) {
				keys = key.GetSubKeyNames();
			}
			foreach (string key in keys) {
				Guid clsid;
				try {
					clsid = new Guid(key);
				} catch {
					continue;
				}
				this.clsids.Add(clsid);
			}
			foreach (KeyValuePair<Guid, WMPPlugin> item in this.plugins) {
				if (!this.clsids.Contains(item.Key))
					Delete(item.Value);
			}
			foreach (Guid clsid in this.clsids) {
				if (!this.plugins.ContainsKey(clsid))
					Create(clsid);
			}
		}

		WMPPlugin Create(Guid clsid) {
			var plugin = new WMPPlugin(this, clsid);
			this.plugins.Add(clsid, plugin);
			foreach (var module in modules) {
				module.Register(plugin);
			}
			return plugin;
		}

		void Delete(WMPPlugin plugin) {
			foreach (var module in modules) {
				module.Unregister(plugin);
			}
			this.plugins.Remove(plugin.CLSID);
		}

		public bool AddModule(IPluginManager module) {
			if (this.modules.Contains(module))
				return false;
			this.modules.Add(module);
			return true;
		}

		public bool RemoveModule(IPluginManager module) {
			return this.modules.Remove(module);
		}

		IEnumerator<WMPPlugin> IEnumerable<WMPPlugin>.GetEnumerator() {
			return this.plugins.Values.GetEnumerator();
		}

		System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
			return this.plugins.Values.GetEnumerator();
		}
	}
}
